﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MessageBoxEx.cs" company="Elilink">
//   Defor
// </copyright>
// <summary>
//   Class MessageBoxEx
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace STDULibrary.Tools
{
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;

    /// <summary>
    ///     Class MessageBoxEx
    /// </summary>
    public class MessageBoxEx
    {
        #region Constants

        /// <summary>
        ///     The W h_ CALLWNDPROCRET
        /// </summary>
        public const int WH_CALLWNDPROCRET = 12;

        #endregion

        #region Static Fields

        /// <summary>
        ///     The hook proc var
        /// </summary>
        private static readonly HookProc HookProcVar;

        /// <summary>
        ///     The _h hook
        /// </summary>
        private static IntPtr _hHook;

        /// <summary>
        ///     The _owner
        /// </summary>
        private static IWin32Window _owner;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes static members of the <see cref="MessageBoxEx" /> class.
        /// </summary>
        static MessageBoxEx()
        {
            HookProcVar = MessageBoxHookProc;
            _hHook = IntPtr.Zero;
        }

        #endregion

        #region Delegates

        /// <summary>
        ///     Delegate HookProc
        /// </summary>
        /// <param name="nCode">The n code.</param>
        /// <param name="wParam">The w param.</param>
        /// <param name="lParam">The l param.</param>
        /// <returns>IntPtr.</returns>
        public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        /// <summary>
        ///     Delegate TimerProc
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="uMsg">The u MSG.</param>
        /// <param name="nIDEvent">The n ID event.</param>
        /// <param name="dwTime">The dw time.</param>
        public delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime);

        #endregion

        #region Enums

        /// <summary>
        ///     Enum CbtHookAction
        /// </summary>
        public enum CbtHookAction
        {
            /// <summary>
            ///     The HCB t_ MOVESIZE
            /// </summary>
            HCBT_MOVESIZE = 0,

            /// <summary>
            ///     The HCB t_ MINMAX
            /// </summary>
            HCBT_MINMAX = 1,

            /// <summary>
            ///     The HCB t_ QS
            /// </summary>
            HCBT_QS = 2,

            /// <summary>
            ///     The HCB t_ CREATEWND
            /// </summary>
            HCBT_CREATEWND = 3,

            /// <summary>
            ///     The HCB t_ DESTROYWND
            /// </summary>
            HCBT_DESTROYWND = 4,

            /// <summary>
            ///     The HCB t_ ACTIVATE
            /// </summary>
            HCBT_ACTIVATE = 5,

            /// <summary>
            ///     The HCB t_ CLICKSKIPPED
            /// </summary>
            HCBT_CLICKSKIPPED = 6,

            /// <summary>
            ///     The HCB t_ KEYSKIPPED
            /// </summary>
            HCBT_KEYSKIPPED = 7,

            /// <summary>
            ///     The HCB t_ SYSCOMMAND
            /// </summary>
            HCBT_SYSCOMMAND = 8,

            /// <summary>
            ///     The HCB t_ SETFOCUS
            /// </summary>
            HCBT_SETFOCUS = 9
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Calls the next hook ex.
        /// </summary>
        /// <param name="idHook">
        /// The id hook.
        /// </param>
        /// <param name="nCode">
        /// The n code.
        /// </param>
        /// <param name="wParam">
        /// The w param.
        /// </param>
        /// <param name="lParam">
        /// The l param.
        /// </param>
        /// <returns>
        /// IntPtr.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// Ends the dialog.
        /// </summary>
        /// <param name="hDlg">
        /// The h DLG.
        /// </param>
        /// <param name="nResult">
        /// The n result.
        /// </param>
        /// <returns>
        /// System.Int32.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);

        /// <summary>
        /// Gets the window text.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="maxLength">
        /// Length of the max.
        /// </param>
        /// <returns>
        /// System.Int32.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxLength);

        /// <summary>
        /// Gets the length of the window text.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <returns>
        /// System.Int32.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <param name="msg">
        /// The MSG.
        /// </param>
        /// <param name="wParam">
        /// The w param.
        /// </param>
        /// <param name="lParam">
        /// The l param.
        /// </param>
        /// <returns>
        /// IntPtr.
        /// </returns>
        [DllImport("User32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// Sets the timer.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <param name="nIDEvent">
        /// The n ID event.
        /// </param>
        /// <param name="uElapse">
        /// The u elapse.
        /// </param>
        /// <param name="lpTimerFunc">
        /// The lp timer func.
        /// </param>
        /// <returns>
        /// UIntPtr.
        /// </returns>
        [DllImport("User32.dll")]
        public static extern UIntPtr SetTimer(IntPtr hWnd, UIntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc);

        /// <summary>
        /// Sets the windows hook ex.
        /// </summary>
        /// <param name="idHook">
        /// The id hook.
        /// </param>
        /// <param name="lpfn">
        /// The LPFN.
        /// </param>
        /// <param name="hInstance">
        /// The h instance.
        /// </param>
        /// <param name="threadId">
        /// The thread id.
        /// </param>
        /// <returns>
        /// IntPtr.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(string text)
        {
            Initialize();
            return MessageBox.Show(text);
        }

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(string text, string caption)
        {
            Initialize();
            return MessageBox.Show(text, caption);
        }

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(string text, string caption, MessageBoxButtons buttons)
        {
            Initialize();
            return MessageBox.Show(text, caption, buttons);
        }

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
        {
            Initialize();
            return MessageBox.Show(text, caption, buttons, icon);
        }

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <param name="defButton">
        /// The def button.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(
            string text,
            string caption,
            MessageBoxButtons buttons,
            MessageBoxIcon icon,
            MessageBoxDefaultButton defButton)
        {
            Initialize();
            return MessageBox.Show(text, caption, buttons, icon, defButton);
        }

        /// <summary>
        /// Shows the specified text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <param name="defButton">
        /// The def button.
        /// </param>
        /// <param name="options">
        /// The options.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(
            string text,
            string caption,
            MessageBoxButtons buttons,
            MessageBoxIcon icon,
            MessageBoxDefaultButton defButton,
            MessageBoxOptions options)
        {
            Initialize();
            return MessageBox.Show(text, caption, buttons, icon, defButton, options);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(IWin32Window owner, string text)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(IWin32Window owner, string text, string caption)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text, caption);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text, caption, buttons);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(
            IWin32Window owner,
            string text,
            string caption,
            MessageBoxButtons buttons,
            MessageBoxIcon icon)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text, caption, buttons, icon);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <param name="defButton">
        /// The def button.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(
            IWin32Window owner,
            string text,
            string caption,
            MessageBoxButtons buttons,
            MessageBoxIcon icon,
            MessageBoxDefaultButton defButton)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text, caption, buttons, icon, defButton);
        }

        /// <summary>
        /// Shows the specified owner.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="caption">
        /// The caption.
        /// </param>
        /// <param name="buttons">
        /// The buttons.
        /// </param>
        /// <param name="icon">
        /// The icon.
        /// </param>
        /// <param name="defButton">
        /// The def button.
        /// </param>
        /// <param name="options">
        /// The options.
        /// </param>
        /// <returns>
        /// DialogResult.
        /// </returns>
        public static DialogResult Show(
            IWin32Window owner,
            string text,
            string caption,
            MessageBoxButtons buttons,
            MessageBoxIcon icon,
            MessageBoxDefaultButton defButton,
            MessageBoxOptions options)
        {
            _owner = owner;
            Initialize();
            return MessageBox.Show(owner, text, caption, buttons, icon, defButton, options);
        }

        /// <summary>
        /// Unhooks the windows hook ex.
        /// </summary>
        /// <param name="idHook">
        /// The id hook.
        /// </param>
        /// <returns>
        /// System.Int32.
        /// </returns>
        [DllImport("user32.dll")]
        public static extern int UnhookWindowsHookEx(IntPtr idHook);

        #endregion

        #region Methods

        /// <summary>
        /// Centers the window.
        /// </summary>
        /// <param name="hChildWnd">
        /// The h child WND.
        /// </param>
        private static void CenterWindow(IntPtr hChildWnd)
        {
            var recChild = new Rectangle(0, 0, 0, 0);
            GetWindowRect(hChildWnd, ref recChild);

            int width = recChild.Width - recChild.X;
            int height = recChild.Height - recChild.Y;

            var recParent = new Rectangle(0, 0, 0, 0);
            GetWindowRect(_owner.Handle, ref recParent);

            var ptCenter = new Point(0, 0)
            {
                X = recParent.X + ((recParent.Width - recParent.X) / 2),
                Y = recParent.Y + ((recParent.Height - recParent.Y) / 2)
            };

            var ptStart = new Point(0, 0) { X = ptCenter.X - (width / 2), Y = ptCenter.Y - (height / 2) };

            ptStart.X = (ptStart.X < 0) ? 0 : ptStart.X;
            ptStart.Y = (ptStart.Y < 0) ? 0 : ptStart.Y;

            MoveWindow(hChildWnd, ptStart.X, ptStart.Y, width, height, false);
        }

        /// <summary>
        /// Gets the window rect.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <param name="lpRect">
        /// The lp rect.
        /// </param>
        /// <returns>
        /// <c>true</c> if XXXX, <c>false</c> otherwise
        /// </returns>
        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect);

        /// <summary>
        ///     Initializes this instance.
        /// </summary>
        /// <exception cref="System.NotSupportedException">multiple calls are not supported</exception>
        private static void Initialize()
        {
            if (_hHook != IntPtr.Zero)
            {
                throw new NotSupportedException("multiple calls are not supported");
            }

            if (_owner != null)
            {
#pragma warning disable 618
                _hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, HookProcVar, IntPtr.Zero, AppDomain.GetCurrentThreadId());
#pragma warning restore 618
            }
        }

        /// <summary>
        /// Messages the box hook proc.
        /// </summary>
        /// <param name="nCode">
        /// The n code.
        /// </param>
        /// <param name="wParam">
        /// The w param.
        /// </param>
        /// <param name="lParam">
        /// The l param.
        /// </param>
        /// <returns>
        /// IntPtr.
        /// </returns>
        private static IntPtr MessageBoxHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                return CallNextHookEx(_hHook, nCode, wParam, lParam);
            }

            var msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT));
            IntPtr hook = _hHook;

            if (msg.message == (int)CbtHookAction.HCBT_ACTIVATE)
            {
                try
                {
                    CenterWindow(msg.hwnd);
                }
                finally
                {
                    UnhookWindowsHookEx(_hHook);
                    _hHook = IntPtr.Zero;
                }
            }

            return CallNextHookEx(hook, nCode, wParam, lParam);
        }

        /// <summary>
        /// Moves the window.
        /// </summary>
        /// <param name="hWnd">
        /// The h WND.
        /// </param>
        /// <param name="x">
        /// The x.
        /// </param>
        /// <param name="y">
        /// The y.
        /// </param>
        /// <param name="nWidth">
        /// Width of the n.
        /// </param>
        /// <param name="nHeight">
        /// Height of the n.
        /// </param>
        /// <param name="bRepaint">
        /// if set to <c>true</c> [b repaint].
        /// </param>
        /// <returns>
        /// System.Int32.
        /// </returns>
        [DllImport("user32.dll")]
        private static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool bRepaint);

        #endregion

        /// <summary>
        ///     Struct CWPRETSTRUCT
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct CWPRETSTRUCT
        {
            /// <summary>
            ///     The l result
            /// </summary>
            public IntPtr lResult;

            /// <summary>
            ///     The l param
            /// </summary>
            public IntPtr lParam;

            /// <summary>
            ///     The w param
            /// </summary>
            public IntPtr wParam;

            /// <summary>
            ///     The message
            /// </summary>
            public uint message;

            /// <summary>
            ///     The HWND
            /// </summary>
            public IntPtr hwnd;
        };
    }
}